package com.apes.framework.plugin.workflow.lib;

import com.alibaba.fastjson.JSON;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.plugin.workflow.lib.callback.FlowExecuteInterface;
import com.apes.framework.plugin.workflow.parser.model.Node;
import com.apes.framework.plugin.workflow.parser.model.Workflow;
import com.apes.framework.util.JpaUtil;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.Tools;
import com.apestech.api.workflow.WorkFlow;
import com.apestech.api.workflow.lib.element.FlowTask;
import com.apestech.api.workflow.lib.element.WorkProcessEngine;
import com.apestech.framework.json.JSONObject;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.apes.framework.plugin.workflow.parser.model.Workflow.getWorkflow;
import static com.apes.framework.util.MapUtil.mapper;

/**
 * 提交全部场景逻辑执行类
 *
 * @author hup
 * @create 2018-07-24-10:58
 */
public class WorkPost {

    private WorkProcessEngine engine;
    private Map webmap;
    private Map mapping;
    private JSONObject input;
    private WorkFlow workFlow;
    private Task task;
    private String wf_content;
    private String wf_variable_prefix;
    private FlowTask flowTask;
    private ProcessInstance processInstance;
    private String state;
    private FlowExecuteInterface flowExecuteInterface;
    private Workflow wf;
    private List operGateData;
    private String operGateName;

    public WorkPost(WorkProcessEngine engine, String taskId, String wf_content, String wf_variable_prefix, FlowExecuteInterface flowExecuteInterface) throws Exception {
        this.engine=engine;
        this.workFlow=new WorkFlow(engine);
        this.wf_content=wf_content;
        this.wf_variable_prefix=wf_variable_prefix;
        this.flowExecuteInterface=flowExecuteInterface;
        this.input=new JSONObject();
        input.put("taskId", taskId);
        this.flowTask=new FlowTask(engine, input);
        if(this.flowTask.getTask()==null){
            throw new Exception("当前任务结点不存在，请检查！");
        }
        this.flowTask.getExecNode();
        this.task= flowTask.getTask();
        this.processInstance=flowTask.getProcessInstance();
        this.state=flowTask.getState();
        input.put("processInstanceId",flowTask.getProcessInstanceId());
    }

    public WorkPost(WorkProcessEngine engine, Task task, String wf_content, String wf_variable_prefix, FlowExecuteInterface flowExecuteInterface) throws Exception {
        this.engine=engine;
        this.workFlow=new WorkFlow(engine);
        this.wf_content=wf_content;
        this.wf_variable_prefix=wf_variable_prefix;
        this.flowExecuteInterface=flowExecuteInterface;
        this.input=new JSONObject();
        this.task= task;
        input.put("processInstanceId",task.getProcessInstanceId());
    }

    public void executeTask(Map variables) throws Exception {
        if(flowTask.getTask().isSuspended()){
            throw new Exception("该任务为挂起状态，不允许做执行操作！");
        }
        webmap=variables;
        if("claim".equals(state) || "assgin".equals(state) || "complete".equals(state) ){
            if (!webmap.containsKey("taskExecutor") || Tools.isNull(webmap.get("taskExecutor"))) {
                throw new RuntimeException("任务执行人不允许为空！");
            }
        }
        wf = getWorkflow(processInstance.getProcessDefinitionKey());
        Node node = wf.getTaskNode(task.getTaskDefinitionKey());

        //第一步：Mapping
        mapping = OAUtil.mapping(node, webmap);

        //第二步：执行任务
        boolean exec=false;
        input=getExtendJSON(input);
        if(webmap.get("wf_comments")==null){
            webmap.put("wf_comments","");
        }
        if("addSign".equals(state)){
            webmap.put("wf_comments", "加签提交:"+(String)webmap.get("wf_comments"));
            workFlow.execAddSign(input);
        } else if("counterSign".equals(state)){
            webmap.put("wf_comments", "会签提交:"+(String)webmap.get("wf_comments"));
            workFlow.execCounterSign(input);
        } else {
            if("assgin".equals(state)){
                //多个候选用户，其中一个要求执行、做加、转、会场景，要先补签收逻辑
                //engine.getTaskService().claim(flowTask.getTask().getId(), input.getString("loginUserId"));
                //flowTask.setState("claim");
            }
            exec=true;
        }
        List<String> users=null;
        final List gateData=flowExecuteInterface.getGateData(task.getId());
        if(operGateData==null){
            operGateData=gateData;
        }
        if(exec){
            if(!input.getString("taskId").equals(task.getId())){
                input.put("taskId", task.getId());
                input.put("processInstanceId", flowTask.getProcessInstanceId());
            }
            WorkFlowExecute execute=new WorkFlowExecute(workFlow);
            execute.execute(task, node, mapping, webmap, input, flowTask);
            users=execute.getUsers();

            /**设置网关变量**/
            String variableName = flowExecuteInterface.getGateDataVar(task.getId());
            if(operGateName==null){
                operGateName=variableName;
            }
            if (!StringUtils.isEmpty(variableName)) {
                // 可能存在后端直接执行的情况
                String field = wf_variable_prefix + variableName;
                if (webmap.containsKey(field) || webmap.containsKey(variableName)) {
                    engine.getTaskService().setVariableLocal(task.getId(), variableName,
                            webmap.get(field) != null ?  webmap.get(field) : webmap.get(variableName));
                    mapping.put(variableName, webmap.get(field) != null ?  webmap.get(field) : webmap.get(variableName));
                }
            }
            String taskId=input.getString("taskId");
            input.put("oa_var", mapping);
            if(taskId!=null){
                input.put("taskId", taskId);
            }
            workFlow.processTask(input);
        }
        if(users==null){
            users=workFlow.getTaskUserList(input);
        }
        Map convertMap=OAUtil.convertTask(engine, task, wf_content, gateData);

        if(exec){
            //第三步：业务逻辑执行
            node.getAfterEsbIds().stream().forEach(esbId -> {
                flowExecuteInterface.invokeEsb(esbId, webmap);
            });

            users.stream().distinct().forEach(userId -> {
                flowExecuteInterface.sendMsg("event:ws.wf.task", mapper("action", "executeTask", "userId", userId, "taskId",
                        task.getId(), "task", convertMap));
            });

            //第四步：下一步任务前置处理
            executePreTask();
        } else {
            sendTodoMsg(convertMap);
        }
    }

    private JSONObject getExtendJSON(JSONObject m){
        if(m==null) return null;
        m.putAll(webmap);
        m.put("taskId",m.get("wfTaskId"));
        if(m.get("userId")==null){
            m.put("userId", m.getString("loginUserId"));
            m.put("userName", m.getString("loginUserName"));
        }
        return m;
    }

    /**
     * 功能：下一步任务前置处理
     *
     */
    private void executePreTask() throws Exception {
        //第一步：获取下步节点
        // 非结束任务
        //第二步：任务分配
        // 或 子流程
        //第二步：子流程执行
        // 或 结束任务
        //第二步：结束任务

        //第一步：获取下步节点
        String parentProcId=flowTask.getParentProcessInstanceId();
        String ProcId=flowTask.getProcessInstanceId();
        input.remove("taskId");
        input.put("processInstanceId",ProcId);
        flowTask=new FlowTask(engine, input);
        task= flowTask.getTask();
        if(task==null && parentProcId!=null){
            input.put("processInstanceId",parentProcId);
            flowTask=new FlowTask(engine, input);
            task= flowTask.getTask();
        }
        if(task!=null){
            flowTask.getExecNode();
            processInstance=flowTask.getProcessInstance();
            flowTask.setStateNull();
            state=flowTask.getState();
            input.put("taskId",task.getId());
            input.put("processInstanceId",flowTask.getProcessInstanceId());
            if("org.flowable.bpmn.model.UserTask".equals(flowTask.getNodeType())){
                //第二步：任务分配
                wf = getWorkflow(processInstance.getProcessDefinitionKey());
                Node next = wf.getTaskNode(task.getTaskDefinitionKey());
                if (next == null) throw new RuntimeException("任务节点：" + task.getTaskDefinitionKey() + " 没有配置！");
                if(!("noAssgin".equals(state) || ("assgin".equals(state)) || ("complete".equals(state)) )){
                    throw new Exception("当前结点状态不允许执行赋用户操作！");
                }


                if (next.isAutomatic()) {
                    // 保证自动执行节点前置ESB被执行
                    next.getBeforeEsbIds().stream().forEach(esbId -> {
                        flowExecuteInterface.invokeEsb(esbId, webmap);
                    });

                    extendVariables(webmap);
                    executeTask(webmap);
                    return;
                }

                List<String> users = new ArrayList<>();
                mapping = OAUtil.mapping(next, webmap);
                if (!Tools.isNull(mapping.get("assignee"))) { //业务数据中指定受理人
                    String userId = (String) mapping.get("assignee");
                    users.add(userId);
                } else if (!Tools.isNull(mapping.get("candidates"))) { //业务数据中指定候选人
                    List candidates = JSON.parseArray((String) mapping.get("candidates"));
                    candidates.stream().forEach( userId -> users.add((String.valueOf(userId))));
                } else {
                    // 所有当前执行部门均改为任务发起部门
                    String processInitiatingDepartment = (String) engine.getTaskService().getVariable(task.getId(), "processInitiatingDepartment");
//                    String taskExecutorDepartment = (String) engine.getTaskService().getVariable(task.getId(), "taskExecutorDepartment");
                    String taskExecutorPost = (String) engine.getTaskService().getVariable(task.getId(), "taskExecutorPost");
                    List<Map> rows = next.getUsers(webmap, processInitiatingDepartment, taskExecutorPost);
                    if (rows.isEmpty()) {
                        throw new RuntimeException("任务节点：【" + task.getTaskDefinitionKey() + "：" + task.getName() +  "】没有办理人！");
                    }
                    users.addAll(rows.stream().distinct().map(new Function<Map, String>() {
                        @Override
                        public String apply(Map user) {
                            return user.get("id").toString();
                        }
                    }).collect(Collectors.toList()));
                }

                List gateData=flowExecuteInterface.getGateData(task.getId());
                Map convertMap=OAUtil.convertTask(engine, task, wf_content, gateData);
                users.stream().distinct().forEach(userId -> {
                    try {
                        userId=workFlow.getCommission(userId);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    if (users.size() == 1) engine.getTaskService().setAssignee(task.getId(), userId); //指定受理人
                    else engine.getTaskService().addCandidateUser(task.getId(), userId); //指定候选人
                    flowExecuteInterface.sendMsg("event:ws.wf.task", mapper("action", "executePreTask", "userId", userId,   //前面逻辑调用需要指定
                            "taskId", task.getId(), "task", convertMap));
                });
                flowTask.setState("assgin");

                if (next.getContent() != null) {
                    Map m = OAUtil.content(next, webmap);
                    if (m.size() != 0) engine.getTaskService().setVariableLocal(task.getId(), wf_content, JSON.toJSONString(m));
                }

                // 执行前置任务，防止因为提前手动停单导致无法分配代办人
                next.getBeforeEsbIds().stream().forEach(esbId -> {
                    flowExecuteInterface.invokeEsb(esbId, webmap);
                });
            }
        } else {
            //执行结束任务
            Node end = wf.getEndNode();
            if (end != null) {
                //业务逻辑处理
                end.getEsbIds().stream().forEach(esbId -> {
                    flowExecuteInterface.invokeEsb(esbId, webmap);
                });
            }
        }
    }

    private void extendVariables(Map variables) {
        if (!variables.containsKey("innerBusinessType") || !variables.containsKey("innerBusinessKey")) return;
        Class clazz = JpaUtil.getDomainClass((String) variables.get("innerBusinessType"));
        if (clazz == null) return;
        CustomRepository repository = JpaUtil.getRepository(clazz.getName());
        if (repository == null) return;
        Object source = repository.findOne(JpaUtil.getId((String) variables.get("innerBusinessType"), (String) variables.get("innerBusinessKey")));
        if (source == null) return;
        Map m = MapUtil.mapped(source);
        m.forEach((k, v) -> {
            if(variables.get(k)==null){
                variables.put(k, v);
            }

        });
    }

    public void sendExtendMsg(JSONObject input, boolean stop) throws Exception {
         List gateData=null;
        if(!stop){
            gateData=flowExecuteInterface.getGateData(task.getId());
        }
        Map convertMap=OAUtil.convertTask(engine, task, wf_content, gateData);
        convertMap.put("createTime", workFlow.getTaskUpdateTime(task.getId()));
        input.remove("processInstanceId");
        flowExecuteInterface.sendMsg("event:ws.wf.task", mapper("action", "executeTask", "userId", input.getString("loginUserId"),
                "taskId", task.getId(), "task", convertMap));
        if(!stop){
            sendTodoMsg(convertMap);
        }
    }

    private void sendTodoMsg(Map convertMap) throws Exception {
        ArrayList<String> users=workFlow.getTaskUserList(input);
        users.stream().forEach(userId -> {
            flowExecuteInterface.sendMsg("event:ws.wf.task", mapper("action", "executePreTask", "userId", userId,
                    "taskId", task.getId(), "task", convertMap));
        });
    }

    public Task getTask() {
        return task;
    }

    public List getOperGateData() {
        return operGateData;
    }

    public String getOperGateName() {
        return operGateName;
    }

    public Map getWebmap() {
        return webmap;
    }
}



